package com.googlecode.tidej.client.editor;

import com.google.gwt.dom.client.Style;
import com.google.gwt.dom.client.Style.Display;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.event.dom.client.ChangeEvent;
import com.google.gwt.event.dom.client.ChangeHandler;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.Anchor;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.DockLayoutPanel;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.LayoutPanel;
import com.google.gwt.user.client.ui.ListBox;
import com.googlecode.tidej.client.TidejController;
import com.googlecode.tidej.client.project.NewProjectDialog;
import com.googlecode.tidej.client.project.OpenProjectDialog;
import com.googlecode.tidej.client.util.Base64;
import com.googlecode.tidej.shared.model.Artifact;
import com.googlecode.tidej.shared.model.Library;
import com.googlecode.tidej.shared.model.Operation;
import com.googlecode.tidej.shared.model.Property;
import com.googlecode.tidej.shared.model.Type;

public class ArtifactEditor extends Composite {
  private static final String MENU_SEPARATOR ="\u00a0\u00a0\u00a0\u00a0\u2500\u2500";
  TidejController controller;
  DockLayoutPanel mainPanel = new DockLayoutPanel(Unit.PX);
  FlowPanel topPanel = new FlowPanel();
  HTML titleLabel = new HTML();
  LayoutPanel contentPanel = new LayoutPanel(); //30, Unit.PX);
  //CodeEditor codeEditor;
  HtmlEditor htmlBodyEditor;
  Artifact artifact;
  Anchor connectionLink = new Anchor("Disconnected", "#");
  Anchor sourceLink = new Anchor("Source", "#", "source");
  Anchor exportLink = new Anchor("Export", "#", "export");
  Anchor runLink = new Anchor("Run", "#", "run");

  OperationDetailWidget operationDetailWidget;
  TypeDetailWidget typeDetailWidget;
  ListBox menu = new ListBox();
  FlowPanel menuPanel = new FlowPanel();
  
  //FlowPanel menuBox = new FlowPanel();
  boolean readOnly;
  Anchor documentationLink = new Anchor();
  FlowPanel libraryPanel = new FlowPanel();
  
  public ArtifactEditor(final TidejController controller) {
   // codeEditor = new CodeEditor(controller);
    htmlBodyEditor = new HtmlEditor(controller);
    this.controller = controller;
    operationDetailWidget = new OperationDetailWidget(this);
    typeDetailWidget = new TypeDetailWidget(this);


    runLink.setStyleName("link-button");
    runLink.addStyleName("run");
    topPanel.add(runLink);
    runLink.setVisible(false);
    runLink.addClickHandler(new ClickHandler() {
      @Override
      public void onClick(ClickEvent event) {
        save();
        runLink.setHref("data:text/html;base64," + Base64.encode(controller.getProjectManager().getHtmlAndScript()));
      }});
    exportLink.setStyleName("link-button");
    topPanel.add(exportLink);
    exportLink.setVisible(false);
    exportLink.addClickHandler(new ClickHandler() {
      @Override
      public void onClick(ClickEvent event) {
        save();
        exportLink.getElement().setAttribute("download", controller.getProject().getName() + ".html");
        exportLink.setHref("data:application/octet-stream;base64," + Base64.encode(controller.getProjectManager().getHtmlAndScript()));
      }});

    sourceLink.setStyleName("link-button");
    topPanel.add(sourceLink);
    sourceLink.setVisible(false);
    sourceLink.addClickHandler(new ClickHandler() {
      @Override
      public void onClick(ClickEvent event) {
        save();
        sourceLink.setHref("data:text/plain;base64," + Base64.encode(controller.getProjectManager().getHtmlAndScript()));
      }});
    

    connectionLink.setStyleName("link-button");
    connectionLink.addClickHandler(new ClickHandler() {
      @Override
      public void onClick(ClickEvent event) {
        save();
        controller.connect();
      }});
    topPanel.add(connectionLink);
    connectionLink.setVisible(false);

    mainPanel.addNorth(topPanel, 40);
    Style style = topPanel.getElement().getStyle();
    style.setBackgroundColor("#ccc");
    topPanel.add(menuPanel);
    topPanel.add(titleLabel);
    mainPanel.add(contentPanel);
    titleLabel.getElement().getStyle().setPadding(10, Unit.PX);
    menuPanel.getElement().getStyle().setFloat(Style.Float.LEFT);
    menuPanel.getElement().getStyle().setPadding(8, Unit.PX);
 //   codeEditor.getElement().getStyle().setBorderWidth(0, Unit.PX);
    initWidget(mainPanel);
    menuPanel.add(menu);
    menu.addChangeHandler(new ChangeHandler() {
      @Override
      public void onChange(ChangeEvent event) {
        String action = menu.getItemText(menu.getSelectedIndex());
        menu.setSelectedIndex(0);
        action(action);
      }
    });
    setArtifact(null, true);
    libraryPanel.getElement().getStyle().setPadding(10, Unit.PX);
    libraryPanel.add(documentationLink);
    
//    tabPanel.addSelectionHandler(new SelectionHandler<Integer>() {
//      @Override
//      public void onSelection(SelectionEvent<Integer> event) {
//        codeEditor.codeMirror.refresh();
//      }
//      
//    });
  }

  
  
  public void action(String action) {
    if (("Add Type").equals(action)) {
      new OperationSignatureDialog(controller).createType();
    } else if ("Add Operation".equals(action)) {
      Type type;
      if (artifact instanceof Operation) {
        type = ((Operation) artifact).getOwner();
      } else {
        type = (Type) artifact;
      }
      new OperationSignatureDialog(controller).createOperation(type);
    } else if (("Close Project").equals(action)) {
      controller.closeProject(true);
    } else if (("Deploy Project").equals(action)) {
      controller.upload();
    } else if (("Delete Project").equals(action) && Window.confirm(
        "Really delete project '" + controller.getProject().getName() + 
        "' and all its classes and resources? This cannot be undone.")) {
      controller.deleteProject();
    } else if ("Delete Type".equals(action) && Window.confirm(
        "Delete type '" + artifact + "' and properties and operations?")) {
      Type type = (Type) artifact;
      artifact = null;
      controller.deleteType(type);
    } else if ("Delete Operation".equals(action) && Window.confirm(
        "Delete operation '" + artifact + "'?")) {
      Operation operation = (Operation) artifact;
      artifact = null;
      controller.deleteOperation(operation);
    } else if ("Open Project".equals(action)) {
      new OpenProjectDialog(controller).run();
    } else if ("New Project".equals(action)) {
      new NewProjectDialog(controller).run();
    } else if ("Search / Replace".equals(action)) {
      new SearchAndReplaceDialog(this).run();
    }
  }

  /**
   * Call this method to save the editor contents.
   */
  public void save() {
    if (readOnly) {
      return;
    }
    // Operation, Type
    if (artifact instanceof Type) {
      if (typeDetailWidget.checkModifications()) {
        controller.saveCode((Type) artifact);
      }
    } else if (artifact instanceof Operation) {
      if (operationDetailWidget.checkModifications() 
          //| codeEditor.checkModifications()
          ) {
        controller.saveCode(((Operation) artifact).getOwner());
      }
    } else if (artifact == controller.getProject()) {
      if (//codeEditor.checkModifications() |
          htmlBodyEditor.checkModifications()) {
        controller.saveCode();
      }
    } 
  }


  public void setArtifact(Artifact newArtifact, boolean save) {
    save();
    contentPanel.remove(libraryPanel);
  //  tabPanel.remove(codeEditor);
    contentPanel.remove(operationDetailWidget);
    contentPanel.remove(htmlBodyEditor);
    contentPanel.remove(typeDetailWidget);

    if (newArtifact instanceof Property) {
      newArtifact = ((Property) newArtifact).getOwner();
    }
    artifact = newArtifact;
    
    menu.clear();
    menu.addItem(getArtifactCategrory());
    menu.addItem("\u00a0\u00a0\u00a0\u00a0\u2500\u2500");
    menu.addItem("Open Project");
    menu.addItem("New Project");
    menu.setSelectedIndex(0);

    if (artifact == null) {
      titleLabel.setText("");
      runLink.setVisible(false);
      exportLink.setVisible(false);
      sourceLink.setVisible(false);
      connectionLink.setVisible(false);
      readOnly = true;
      return;
    }

    
    readOnly = artifact.getLibrary() != controller.getProject();

    if (controller.getProject() != null) {
//      String location = Window.Location.getHref();
//      int hash = location.indexOf('#');
//      if (hash != -1) {
//        location = location.substring(0, hash);
//      }
//      char deli = location.indexOf('?') == -1 ? '?' : '&';
//      String url = location + deli + "run=" + controller.getModel().
//          getProject().getName() + "&nocache=" + Math.random();
//      runLink.setHref(url);
      connectionLink.getElement().getStyle().setDisplay(Display.BLOCK);
      runLink.getElement().getStyle().setDisplay(Display.BLOCK);
      exportLink.getElement().getStyle().setDisplay(Display.BLOCK);
      sourceLink.getElement().getStyle().setDisplay(Display.BLOCK);
      menu.addItem("Close Project");
      if (artifact == controller.getProject()) {
        menu.addItem("Delete Project");
      }
      menu.addItem(MENU_SEPARATOR);
      menu.addItem("Deploy Project");
      menu.addItem(MENU_SEPARATOR);
      menu.addItem("Add Type");
    }
    
    titleLabel.setHTML(artifact.getTitle());
    if (artifact instanceof Type) {
      Type type = (Type) artifact;
      contentPanel.add(typeDetailWidget);//, "Details");
      typeDetailWidget.setType(type);
      if (!readOnly) {
        menu.addItem("Delete Type");
        menu.addItem(MENU_SEPARATOR);
        menu.addItem("Add Operation");
        menu.addItem(MENU_SEPARATOR);
        menu.addItem("Search / Replace");
      }
    } else if (artifact instanceof Operation) {
      menu.addItem(MENU_SEPARATOR);
      menu.addItem("Add Operation");
      menu.addItem("Delete Operation");
      menu.addItem(MENU_SEPARATOR);
      menu.addItem("Search / Replace");
      Operation operation = (Operation) artifact;
      contentPanel.add(operationDetailWidget); //, "Details");
      operationDetailWidget.setOperation(operation);
      if (!readOnly) {
//        tabPanel.add(codeEditor, "Implementation");
//        codeEditor.setOperation(operation);
      }
    } else if (artifact instanceof Library) {
      Library lib = (Library) artifact;
      if (lib == controller.getProject()) {
        contentPanel.add(htmlBodyEditor); //, "index.html");
//        tabPanel.add(codeEditor, "main.js");
//        tabPanel.selectTab(1);
//        codeEditor.setOperation(p.getMain());
        htmlBodyEditor.setHtml(controller.getProjectManager());
        menu.addItem(MENU_SEPARATOR);
        menu.addItem("Search / Replace");
      } else if (lib.getDocumentationUrl() != null) {
        documentationLink.setHref(lib.getDocumentationUrl());
        documentationLink.setText(lib.getDocumentationUrl());
        documentationLink.setTarget(lib.getName() + "_doc");
        contentPanel.add(libraryPanel); 
      }
    }
    
  }

  private String getArtifactCategrory() {
    if (artifact == null || artifact == controller.getProject()) {
      return "Project";
    } else if (artifact instanceof Library) {
      return "Library";
    } else if (artifact instanceof Operation) {
      Operation op = (Operation) artifact;
      return (op.getOwner().getConstructor() == op) ? "Constructor" : "Operation";
    } else if (artifact instanceof Type) {
      return "Type";
    } else {
      return "Artifact";
    }
  }



  public Artifact getArtifact() {
    return artifact;
  }



  public void setOnlineState(String url, boolean connected) {
    if (url == null) {
      connectionLink.setText("Disconnected");
      connectionLink.removeStyleName("disconnected");
      connectionLink.removeStyleName("connected");
    } else {
      connectionLink.setText(url);
      if (connected) {
        connectionLink.removeStyleName("disconnected");
        connectionLink.addStyleName("connected");
      } else {
        connectionLink.removeStyleName("connected");
        connectionLink.addStyleName("disconnected");
      }
    }
  }
}
